// #define DONOT_USE_RSEQ
/* 测量nop执行效率 */
#define _GNU_SOURCE
#include <assert.h>
#include <libapfsds.h>
#include <dbg.h>
#include <dbg/ptest.h>
#include <pthreadalt.h>
#include <ddblk_plat.h>
#include <add.h>
#include <htable.h>
#include <nop.h>
#include <queue.h>
#include <stack.h>
#include <rbtree.h>
#include <tlsf.h>
#include <dbg.h>
#ifndef HDEF_LITTLECORE_OFFST
	#warning "undefined LITTLECORE_OFFST"
	#define HDEF_LITTLECORE_OFFST	0
#endif
#define TEST_NR			(1000000000)
#define TEST_THREADNR		(HDEF_NATIVE_CPU_NR)
static apfsds_t *apfsds;

word_t test_nr;
SAI NO_UNROLL void *tnopeff(void *args){
	/* WRAP_ARGS(ar, NULL); */
	/* word_t ret; */
	word_t onops = nop_nrs;
	word_t nops;
	/* 越大等待时间越短 */
	word_t nr = atm_ld(&test_nr);
	for(word_t j = 0; j < nr; j++){
		word_t i = 0;
		nops = hash(j) & (onops - 1);
		do{
			NOP();
		} while(++i < nops);
	}

	return NULL;
}


static float *test_nopres_mops_bycid;
static word_t test_nop_threads_nr;
static tid_t test_nop_threads_tid;
static float *test_cycles_perns;
static void * test_nop_wrapper(void *arg){
	word_t target_thread_nr = (word_t)arg;
	struct timespec ts, td;
	word_t cnt;
	tid_t tid = faa(&test_nop_threads_tid, 1);
	cid_t cid = (tid + HDEF_LITTLECORE_OFFST) % HDEF_NATIVE_CPU_NR;
	BIND_CORE(cid);
	faa(&test_nop_threads_nr, 1);
	while (atm_ld(&test_nop_threads_nr) <= target_thread_nr);
	TIME_STAMP(&ts);
	cnt = rdcnt_relax();
	tnopeff(arg);
	cnt = rdcnt_relax() - cnt;
	TIME_STAMP(&td);
	time_t tm = TIME_DELTA_US(&ts, &td);
	test_nopres_mops_bycid[cid] = ((float)test_nr)  / tm;
	test_cycles_perns[cid] = (float)cnt / TIME_DELTA_NS(&ts, &td);
	/* dbg_printf("[%u]%s %lu %f\n", cid, "nopeff", nop_nrs, ((float)test_nr)  / tm); */
	return (void *)tm;
}

// int c[next_pow2(5)];

static void test_nopeff(word_t nop_nr){
	pthread_t p[TEST_THREADNR];
	void *tret[TEST_THREADNR];
	float test_freqs[TEST_THREADNR];
	test_cycles_perns = test_freqs;
	word_t sum;
	word_t half_threads = 1;
	nop_nrs = next_pow2(nop_nr);
	
	/* 防止临界区太长 */
	word_t div = nop_nr;
	/* if (div == 0) div = 1; */
	test_nr = TEST_NR / div;
	float result[TEST_THREADNR];
	test_nopres_mops_bycid = result;
	for (word_t thread_nr = TEST_THREADNR; thread_nr <= TEST_THREADNR; thread_nr += half_threads){
		BIND_CORE(HDEF_LITTLECORE_OFFST);
		nop_cnt_reset();
		atm_st(&test_nop_threads_nr, 0);
		atm_st(&test_nop_threads_tid, 0);
		sum = 0;
		for (word_t idx = 0; idx < thread_nr; idx++){
			pthread_create(&p[idx], NULL, test_nop_wrapper, (void *)thread_nr);
		}
		faa(&test_nop_threads_nr, 1);
		for (word_t idx = 0; idx < thread_nr; idx++)
		{
			pthread_join(p[idx], &tret[idx]);
		}
		for (word_t idx = 0; idx < thread_nr; idx++){
			dbg_printf("[%lu]%s %lu %f cycle/ns: %f\n", idx, "nopeff", nop_nrs, test_nopres_mops_bycid[idx] / test_nopres_mops_bycid[HDEF_LITTLECORE_OFFST], test_cycles_perns[idx]);
			sum += (word_t)tret[idx];
		}
		if (sum == 0) fatal ("[%lu]%s time == 0, error\n", thread_nr, "nop");
		/* dbg_printf("[%lu]%s(%lu)\t%lu ms\t\t%lu kop/s\n", thread_nr, #func, (word_t)op_nr, sum/thread_nr, (op_nr)  / (sum / thread_nr) ); */
		dbg_printf("total %s %lu %f\n-----------------\n", "nopeff", nop_nr, ((float)test_nr)   / (sum / thread_nr) );
		if (thread_nr != 1 && is_powerof2(thread_nr)) half_threads = thread_nr >> 1;
		CLR_BIND();
	}
	test_nopres_mops_bycid = NULL;
	/* dbg_printf("\n"); */							
}




/* 这个必须定制好 */

int main(){
	apfsds = apfsds_create();
	nop_execution = TEST_NR;
	test_nopeff(1);
	test_nopeff(10);
	test_nopeff(100);
	test_nopeff(1000);
	test_nopeff(10000);
	test_nopeff(100000);

	apfsds_destroy(apfsds);
	// printf("all test passed\n");
	return 0;
}